Kuasai pengujian komponen React dengan React Testing Library. Pelajari praktik terbaik untuk menulis pengujian yang efektif dan mudah dipelihara yang berfokus pada perilaku pengguna dan aksesibilitas.
React Testing Library: Praktik Terbaik Pengujian Komponen untuk Tim Global
Di dunia pengembangan web yang terus berkembang, memastikan keandalan dan kualitas aplikasi React Anda adalah hal yang terpenting. Hal ini terutama berlaku untuk tim global yang mengerjakan proyek dengan basis pengguna yang beragam dan persyaratan aksesibilitas. React Testing Library (RTL) menyediakan pendekatan yang kuat dan berpusat pada pengguna untuk pengujian komponen. Berbeda dengan metode pengujian tradisional yang berfokus pada detail implementasi, RTL mendorong Anda untuk menguji komponen Anda sebagaimana pengguna akan berinteraksi dengannya, yang mengarah pada pengujian yang lebih kuat dan mudah dipelihara. Panduan komprehensif ini akan mendalami praktik terbaik untuk memanfaatkan RTL dalam proyek React Anda, dengan fokus pada membangun aplikasi yang sesuai untuk audiens global.
Mengapa Menggunakan React Testing Library?
Sebelum mendalami praktik terbaik, penting untuk memahami mengapa RTL menonjol dari pustaka pengujian lainnya. Berikut adalah beberapa keuntungan utamanya:
- Pendekatan Berpusat pada Pengguna: RTL memprioritaskan pengujian komponen dari perspektif pengguna. Anda berinteraksi dengan komponen menggunakan metode yang sama seperti yang dilakukan pengguna (misalnya, mengklik tombol, mengetik di kolom input), memastikan pengalaman pengujian yang lebih realistis dan andal.
- Berfokus pada Aksesibilitas: RTL mempromosikan penulisan komponen yang dapat diakses dengan mendorong Anda untuk mengujinya dengan cara yang mempertimbangkan pengguna dengan disabilitas. Ini sejalan dengan standar aksesibilitas global seperti WCAG.
- Mengurangi Pemeliharaan: Dengan menghindari pengujian detail implementasi (misalnya, state internal, panggilan fungsi spesifik), pengujian RTL lebih kecil kemungkinannya untuk rusak saat Anda merefaktor kode Anda. Hal ini mengarah pada pengujian yang lebih mudah dipelihara dan tangguh.
- Desain Kode yang Lebih Baik: Pendekatan RTL yang berpusat pada pengguna sering kali mengarah pada desain komponen yang lebih baik, karena Anda dipaksa untuk memikirkan bagaimana pengguna akan berinteraksi dengan komponen Anda.
- Komunitas dan Ekosistem: RTL memiliki komunitas yang besar dan aktif, menyediakan banyak sumber daya, dukungan, dan ekstensi.
Menyiapkan Lingkungan Pengujian Anda
Untuk memulai dengan RTL, Anda perlu menyiapkan lingkungan pengujian Anda. Berikut adalah pengaturan dasar menggunakan Create React App (CRA), yang sudah dikonfigurasi sebelumnya dengan Jest dan RTL:
npx create-react-app my-react-app
cd my-react-app
npm install --save-dev @testing-library/react @testing-library/jest-dom
Penjelasan:
- `npx create-react-app my-react-app`: Membuat proyek React baru menggunakan Create React App.
- `cd my-react-app`: Menavigasi ke dalam direktori proyek yang baru dibuat.
- `npm install --save-dev @testing-library/react @testing-library/jest-dom`: Menginstal paket RTL yang diperlukan sebagai dependensi pengembangan. `@testing-library/react` menyediakan fungsionalitas inti RTL, sedangkan `@testing-library/jest-dom` menyediakan pencocok Jest yang berguna untuk bekerja dengan DOM.
Jika Anda tidak menggunakan CRA, Anda perlu menginstal Jest dan RTL secara terpisah dan mengonfigurasi Jest untuk menggunakan RTL.
Praktik Terbaik Pengujian Komponen dengan React Testing Library
1. Tulis Pengujian yang Menyerupai Interaksi Pengguna
Prinsip inti RTL adalah menguji komponen sebagaimana pengguna akan menggunakannya. Ini berarti berfokus pada apa yang dilihat dan dilakukan pengguna, bukan detail implementasi internal. Gunakan objek `screen` yang disediakan oleh RTL untuk mencari elemen berdasarkan teks, peran, atau label aksesibilitasnya.
Contoh: Menguji Klik Tombol
Katakanlah Anda memiliki komponen tombol sederhana:
// Button.js
import React from 'react';
function Button({ onClick, children }) {
return ;
}
export default Button;
Berikut adalah cara mengujinya menggunakan RTL:
// Button.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Button from './Button';
describe('Button Component', () => {
it('calls the onClick handler when clicked', () => {
const handleClick = jest.fn();
render();
const buttonElement = screen.getByText('Click Me');
fireEvent.click(buttonElement);
expect(handleClick).toHaveBeenCalledTimes(1);
});
});
Penjelasan:
- `render()`: Merender komponen Button dengan handler `onClick` tiruan (mock).
- `screen.getByText('Click Me')`: Mencari dokumen untuk elemen yang berisi teks "Click Me". Ini adalah cara pengguna mengidentifikasi tombol tersebut.
- `fireEvent.click(buttonElement)`: Mensimulasikan event klik pada elemen tombol.
- `expect(handleClick).toHaveBeenCalledTimes(1)`: Memastikan bahwa handler `onClick` dipanggil satu kali.
Mengapa ini lebih baik daripada menguji detail implementasi: Bayangkan Anda merefaktor komponen Button untuk menggunakan handler event yang berbeda atau mengubah state internal. Jika Anda menguji fungsi handler event tertentu, pengujian Anda akan rusak. Dengan berfokus pada interaksi pengguna (mengklik tombol), pengujian tetap valid bahkan setelah refactoring.
2. Prioritaskan Kueri Berdasarkan Maksud Pengguna
RTL menyediakan berbagai metode kueri untuk menemukan elemen. Prioritaskan kueri berikut dalam urutan ini, karena mereka paling mencerminkan bagaimana pengguna memandang dan berinteraksi dengan komponen Anda:
- getByRole: Kueri ini adalah yang paling mudah diakses dan harus menjadi pilihan pertama Anda. Ini memungkinkan Anda menemukan elemen berdasarkan peran ARIA mereka (misalnya, button, link, heading).
- getByLabelText: Gunakan ini untuk menemukan elemen yang terkait dengan label tertentu, seperti kolom input.
- getByPlaceholderText: Gunakan ini untuk menemukan kolom input berdasarkan teks placeholder mereka.
- getByText: Gunakan ini untuk menemukan elemen berdasarkan konten teksnya. Jadilah spesifik dan hindari menggunakan teks generik yang mungkin muncul di banyak tempat.
- getByDisplayValue: Gunakan ini untuk menemukan kolom input berdasarkan nilainya saat ini.
Contoh: Menguji Input Formulir
// Input.js
import React from 'react';
function Input({ label, placeholder, value, onChange }) {
return (
);
}
export default Input;
Berikut cara mengujinya menggunakan urutan kueri yang direkomendasikan:
// Input.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import Input from './Input';
describe('Input Component', () => {
it('updates the value when the user types', () => {
const handleChange = jest.fn();
render();
const inputElement = screen.getByLabelText('Name');
fireEvent.change(inputElement, { target: { value: 'John Doe' } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleChange).toHaveBeenCalledWith(expect.objectContaining({ target: { value: 'John Doe' } }));
});
});
Penjelasan:
- `screen.getByLabelText('Name')`: Menggunakan `getByLabelText` untuk menemukan kolom input yang terkait dengan label "Name". Ini adalah cara yang paling mudah diakses dan ramah pengguna untuk menemukan input.
3. Hindari Menguji Detail Implementasi
Seperti yang disebutkan sebelumnya, hindari menguji state internal, panggilan fungsi, atau kelas CSS tertentu. Ini adalah detail implementasi yang dapat berubah dan dapat menyebabkan pengujian yang rapuh. Fokus pada perilaku komponen yang dapat diamati.
Contoh: Hindari Menguji State Secara Langsung
Daripada menguji apakah variabel state tertentu diperbarui, ujilah apakah komponen merender output yang benar berdasarkan state tersebut. Misalnya, jika komponen menampilkan pesan berdasarkan variabel state boolean, ujilah apakah pesan tersebut ditampilkan atau disembunyikan, bukan menguji variabel state itu sendiri.
4. Gunakan `data-testid` untuk Kasus Tertentu
Meskipun umumnya lebih baik menghindari penggunaan atribut `data-testid`, ada kasus-kasus tertentu di mana mereka dapat membantu:
- Elemen Tanpa Makna Semantik: Jika Anda perlu menargetkan elemen yang tidak memiliki peran, label, atau teks yang bermakna, Anda dapat menggunakan `data-testid`.
- Struktur Komponen yang Kompleks: Dalam struktur komponen yang kompleks, `data-testid` dapat membantu Anda menargetkan elemen tertentu tanpa bergantung pada selektor yang rapuh.
- Pengujian Aksesibilitas: `data-testid` dapat digunakan untuk mengidentifikasi elemen tertentu selama pengujian aksesibilitas dengan alat seperti Cypress atau Playwright.
Contoh: Menggunakan `data-testid`
// MyComponent.js
import React from 'react';
function MyComponent() {
return (
This is my component.
);
}
export default MyComponent;
// MyComponent.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('renders the component container', () => {
render( );
const containerElement = screen.getByTestId('my-component-container');
expect(containerElement).toBeInTheDocument();
});
});
Penting: Gunakan `data-testid` secukupnya dan hanya jika metode kueri lain tidak sesuai.
5. Tulis Deskripsi Pengujian yang Bermakna
Deskripsi pengujian yang jelas dan ringkas sangat penting untuk memahami tujuan setiap pengujian dan untuk men-debug kegagalan. Gunakan nama deskriptif yang dengan jelas menjelaskan apa yang diverifikasi oleh pengujian.
Contoh: Deskripsi Pengujian yang Baik vs. Buruk
Buruk: `it('works')`
Baik: `it('displays the correct greeting message')`
Lebih Baik Lagi: `it('displays the greeting message "Hello, World!" when the name prop is not provided')`
Contoh yang lebih baik dengan jelas menyatakan perilaku yang diharapkan dari komponen dalam kondisi tertentu.
6. Buat Pengujian Anda Tetap Kecil dan Terfokus
Setiap pengujian harus berfokus pada verifikasi satu aspek perilaku komponen. Hindari menulis pengujian yang besar dan kompleks yang mencakup beberapa skenario. Pengujian yang kecil dan terfokus lebih mudah dipahami, dipelihara, dan di-debug.
7. Gunakan Test Doubles (Mock dan Spy) dengan Tepat
Test doubles berguna untuk mengisolasi komponen yang Anda uji dari dependensinya. Gunakan mock dan spy untuk mensimulasikan layanan eksternal, panggilan API, atau komponen lain.
Contoh: Melakukan Mock Panggilan API
// UserList.js
import React, { useState, useEffect } from 'react';
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
async function fetchUsers() {
const response = await fetch('/api/users');
const data = await response.json();
setUsers(data);
}
fetchUsers();
}, []);
return (
{users.map(user => (
- {user.name}
))}
);
}
export default UserList;
// UserList.test.js
import React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import UserList from './UserList';
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve([
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' },
]),
})
);
describe('UserList Component', () => {
it('fetches and displays a list of users', async () => {
render( );
// Wait for the data to load
await waitFor(() => screen.getByText('John Doe'));
expect(screen.getByText('John Doe')).toBeInTheDocument();
expect(screen.getByText('Jane Smith')).toBeInTheDocument();
});
});
Penjelasan:
- `global.fetch = jest.fn(...)`: Melakukan mock pada fungsi `fetch` untuk mengembalikan daftar pengguna yang telah ditentukan sebelumnya. Ini memungkinkan Anda menguji komponen tanpa bergantung pada endpoint API yang sebenarnya.
- `await waitFor(() => screen.getByText('John Doe'))`: Menunggu hingga teks "John Doe" muncul di dokumen. Ini diperlukan karena data diambil secara asinkron.
8. Uji Kasus-Kasus Ekstrem dan Penanganan Error
Jangan hanya menguji skenario yang ideal (happy path). Pastikan untuk menguji kasus-kasus ekstrem, skenario error, dan kondisi batas. Ini akan membantu Anda mengidentifikasi potensi masalah sejak dini dan memastikan komponen Anda menangani situasi tak terduga dengan baik.
Contoh: Menguji Penanganan Error
Bayangkan sebuah komponen yang mengambil data dari API dan menampilkan pesan error jika panggilan API gagal. Anda harus menulis pengujian untuk memverifikasi bahwa pesan error ditampilkan dengan benar ketika panggilan API gagal.
9. Fokus pada Aksesibilitas
Aksesibilitas sangat penting untuk menciptakan aplikasi web yang inklusif. Gunakan RTL untuk menguji aksesibilitas komponen Anda dan memastikan mereka memenuhi standar aksesibilitas seperti WCAG. Beberapa pertimbangan aksesibilitas utama meliputi:
- HTML Semantik: Gunakan elemen HTML semantik (misalnya, `
- Atribut ARIA: Gunakan atribut ARIA untuk memberikan informasi tambahan tentang peran, status, dan properti elemen, terutama untuk komponen kustom.
- Navigasi Keyboard: Pastikan semua elemen interaktif dapat diakses melalui navigasi keyboard.
- Kontras Warna: Gunakan kontras warna yang cukup untuk memastikan teks dapat dibaca oleh pengguna dengan penglihatan rendah.
- Kompatibilitas Pembaca Layar: Uji komponen Anda dengan pembaca layar untuk memastikan mereka memberikan pengalaman yang bermakna dan dapat dipahami bagi pengguna dengan gangguan penglihatan.
Contoh: Menguji Aksesibilitas dengan `getByRole`
// MyAccessibleComponent.js
import React from 'react';
function MyAccessibleComponent() {
return (
);
}
export default MyAccessibleComponent;
// MyAccessibleComponent.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyAccessibleComponent from './MyAccessibleComponent';
describe('MyAccessibleComponent', () => {
it('renders an accessible button with the correct aria-label', () => {
render( );
const buttonElement = screen.getByRole('button', { name: 'Close' });
expect(buttonElement).toBeInTheDocument();
});
});
Penjelasan:
- `screen.getByRole('button', { name: 'Close' })`: Menggunakan `getByRole` untuk menemukan elemen tombol dengan nama yang dapat diakses "Close". Ini memastikan bahwa tombol diberi label dengan benar untuk pembaca layar.
10. Integrasikan Pengujian ke dalam Alur Kerja Pengembangan Anda
Pengujian harus menjadi bagian integral dari alur kerja pengembangan Anda, bukan sesuatu yang dipikirkan belakangan. Integrasikan pengujian Anda ke dalam pipeline CI/CD Anda untuk menjalankan pengujian secara otomatis setiap kali kode di-commit atau di-deploy. Ini akan membantu Anda menemukan bug lebih awal dan mencegah regresi.
11. Pertimbangkan Lokalisasi dan Internasionalisasi (i18n)
Untuk aplikasi global, sangat penting untuk mempertimbangkan lokalisasi dan internasionalisasi (i18n) selama pengujian. Pastikan komponen Anda dirender dengan benar dalam berbagai bahasa dan lokal.
Contoh: Menguji Lokalisasi
Jika Anda menggunakan pustaka seperti `react-intl` atau `i18next` untuk lokalisasi, Anda dapat melakukan mock pada konteks lokalisasi dalam pengujian Anda untuk memverifikasi bahwa komponen Anda menampilkan teks terjemahan yang benar.
12. Gunakan Fungsi Render Kustom untuk Pengaturan yang Dapat Digunakan Kembali
Saat mengerjakan proyek yang lebih besar, Anda mungkin mendapati diri Anda mengulangi langkah-langkah pengaturan yang sama di beberapa pengujian. Untuk menghindari duplikasi, buat fungsi render kustom yang merangkum logika pengaturan umum.
Contoh: Fungsi Render Kustom
// test-utils.js
import React from 'react';
import { render } from '@testing-library/react';
import { ThemeProvider } from 'styled-components';
import theme from './theme';
const AllTheProviders = ({ children }) => {
return (
{children}
);
}
const customRender = (ui, options) =>
render(ui, { wrapper: AllTheProviders, ...options })
// re-export everything
export * from '@testing-library/react'
// override render method
export { customRender as render }
// MyComponent.test.js
import React from 'react';
import { render, screen } from './test-utils'; // Import the custom render
import MyComponent from './MyComponent';
describe('MyComponent', () => {
it('renders correctly with the theme', () => {
render( );
// Your test logic here
});
});
Contoh ini membuat fungsi render kustom yang membungkus komponen dengan ThemeProvider. Ini memungkinkan Anda untuk dengan mudah menguji komponen yang bergantung pada tema tanpa harus mengulangi pengaturan ThemeProvider di setiap pengujian.
Kesimpulan
React Testing Library menawarkan pendekatan yang kuat dan berpusat pada pengguna untuk pengujian komponen. Dengan mengikuti praktik terbaik ini, Anda dapat menulis pengujian yang efektif dan mudah dipelihara yang berfokus pada perilaku pengguna dan aksesibilitas. Ini akan mengarah pada aplikasi React yang lebih kuat, andal, dan inklusif untuk audiens global. Ingatlah untuk memprioritaskan interaksi pengguna, menghindari pengujian detail implementasi, fokus pada aksesibilitas, dan mengintegrasikan pengujian ke dalam alur kerja pengembangan Anda. Dengan menganut prinsip-prinsip ini, Anda dapat membangun aplikasi React berkualitas tinggi yang memenuhi kebutuhan pengguna di seluruh dunia.
Poin-Poin Penting:
- Fokus pada Interaksi Pengguna: Uji komponen sebagaimana pengguna akan berinteraksi dengannya.
- Prioritaskan Aksesibilitas: Pastikan komponen Anda dapat diakses oleh pengguna dengan disabilitas.
- Hindari Detail Implementasi: Jangan menguji state internal atau panggilan fungsi.
- Tulis Pengujian yang Jelas dan Ringkas: Buat pengujian Anda mudah dipahami dan dipelihara.
- Integrasikan Pengujian ke dalam Alur Kerja Anda: Otomatiskan pengujian Anda dan jalankan secara teratur.
- Pertimbangkan Audiens Global: Pastikan komponen Anda berfungsi dengan baik di berbagai bahasa dan lokal.